﻿using System.Collections.Generic;
using CF.CFLog;
using CF.Core;
using UnityEngine;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;

namespace CF.Pool
{
    public class ObjectPool : MonoBehaviour
    {
        /// <summary>
        /// 预制体索引
        /// </summary>
        protected Dictionary<int, PoolList> PrefabPool;

        /// <summary>
        /// 已生成的索引
        /// </summary>
        protected Dictionary<int, PoolList> SpawnedPool;

        /// <summary>
        /// 对象池配置
        /// </summary>
        private PoolConfig poolConfig;

        private void Awake()
        {
            PrefabPool = new Dictionary<int, PoolList>();
            SpawnedPool = new Dictionary<int, PoolList>();
        }

        private void OnDestroy()
        {
            if (poolConfig.IsClearOnSceneUnloaded)
            {
                SceneManager.sceneUnloaded -= SceneManagerOnsceneUnloaded;
            }
        }

        public void Init(PoolConfig config)
        {
            poolConfig = config;
            if (poolConfig.IsClearOnSceneUnloaded)
            {
                SceneManager.sceneUnloaded += SceneManagerOnsceneUnloaded;
            }
        }

        private void SceneManagerOnsceneUnloaded(Scene scene)
        {
            Log.Info($"{name} Is Start ClearDespawnPool On Scene {scene.name} UnLoaded.");
            ClearDespawnPool();
        }

        /// <summary>
        /// 预加载
        /// </summary>
        /// <param name="prefab"></param>
        /// <param name="count"></param>
        public void Preload(GameObject prefab, int count)
        {
            if (count > poolConfig.MaxObjectCount) count = poolConfig.MaxObjectCount;
            for (int i = 0; i < count; i++)
            {
                Spawn(prefab);
            }
        }

        /// <summary>
        /// 生成物体
        /// </summary>
        public GameObject Spawn(GameObject prefab)
        {
            return Spawn(prefab, Vector3.zero, Quaternion.identity);
        }

        /// <summary>
        /// 生成物体
        /// </summary>
        public GameObject Spawn(GameObject prefab, Vector3 pos, Quaternion quaternion, Transform parent = null)
        {
            var instanceID = prefab.GetInstanceID();
            if (PrefabPool.TryGetValue(instanceID, out PoolList poolList))
            {
                var spawnGo = poolList.Spawn(prefab, pos, quaternion, parent == null ? transform : parent);
                SpawnedPool.Add(spawnGo.GetInstanceID(), poolList);
                return spawnGo;
            }

            var newPoolList = new PoolList(poolConfig);
            PrefabPool.Add(instanceID, newPoolList);
            var go = newPoolList.Spawn(prefab, pos, quaternion, parent == null ? transform : parent);
            SpawnedPool.Add(go.GetInstanceID(), newPoolList);
            return go;
        }

        /// <summary>
        /// 回收物体
        /// </summary>
        /// <param name="go"></param>
        /// <returns></returns>
        public void Despawn(GameObject go)
        {
            if (!SpawnedPool.TryGetValue(go.GetInstanceID(), out PoolList poolList))
            {
                Log.Error($"Not found {go.name} in spawnedPool. It will be destroyed.");
                Object.Destroy(go);
                return;
            }

            SpawnedPool.Remove(go.GetInstanceID());
            poolList.Despawn(go);
        }

        /// <summary>
        /// 清理回收对象池
        /// </summary>
        public void ClearDespawnPool()
        {
            foreach (var pool in PrefabPool.Values)
            {
                pool.ClearDespawnPool();
            }

            // PrefabPool.Clear ();
            // foreach (var pool in SpawnedPool.Values) {
            // 	pool.Clear ();
            // }
            //
            // SpawnedPool.Clear ();
        }

        /// <summary>
        /// 销毁对象池
        /// </summary>
        public void Destroy()
        {
            foreach (var pool in PrefabPool.Values)
            {
                pool.Destroy();
            }

            PrefabPool.Clear();
            foreach (var pool in SpawnedPool.Values)
            {
                pool.Destroy();
            }

            SpawnedPool.Clear();
            Destroy(gameObject);
        }
    }
}